package taskit.logic;

import java.util.*;
import java.text.ParseException;

import javafx.application.Platform;

import taskit.storage.Data;
import taskit.test.Log;
import taskit.datastructures.Command;
import taskit.datastructures.CommandAdd;
import taskit.datastructures.CommandComplete;
import taskit.datastructures.CommandDelete;
import taskit.datastructures.CommandDeleteFromArchive;
import taskit.datastructures.CommandUpdate;
import taskit.datastructures.Date;
import taskit.datastructures.Deadline;
import taskit.datastructures.Floating;
import taskit.datastructures.ITaskItObject;
import taskit.datastructures.Schedule;
import taskit.datastructures.Task;
import taskit.gui.GUIDispatcher;

// modify/delete -> 2 linked list -> get object and send to Data
// facade that access parser, suppose you wanna switch another parser
// how can you 

// @author A0087072A
public class Parser implements ITaskItObject {
	public static final String NAME_CLASS = "Parser";
	public static Parser parser = null;
	public TimeMachine undoRedo = new TimeMachine();
	public Data internalData = Data.getInstance();
	public final String DEFAULT_DELIMITER = ";";

	// @author A0087072A
	protected Parser() {
		ParserReturnObject.setDataSource(internalData);
	}

	public static Parser getInstance() {
		Log.add("Parser.java: getInstance()");
		if (parser == null) {
			parser = new Parser();
		}
		return parser;
	}

	// this was the old method i used to test
	public static void main(String[] args) {
		Log.add("Parser.java: main(" + args + ")");
		Parser.getInstance();
		String[] testLines = {
				"add todo; complete assignment 0",
				"add deadline; complete assignment -1; 240320141000",
				"add deadline; complete assignment 1; 270320141000",
				"add event; meeting with professor; "
						+ "250320141000 to 280320141200", "undo", "undo",
				"undo", "undo", "redo", "redo", "redo", "redo", "delete 3",
				"undo", "delete 4", "update 2;; 250320141100;", "complete 1",
				"undo", "search assignment", "search -a assignment",
				"search -d 27032014", "search -d 24032014 to 27032014",
				"delete 2" };
		for (String str : testLines) {
			Parser.passInputString(str);
			LinkedList<Deadline> list = ParserReturnObject
					.getDeadlinesAndSchedules();
			System.out.println("Now Showing DAS");
			if (list != null)
				for (Deadline d : list) {
					System.out.println(d.getName());
				}
			LinkedList<Floating> list2 = ParserReturnObject.getFloatingTasks();
			System.out.println("Now Showing Floating");
			if (list2 != null)
				for (Floating d : list2) {
					System.out.println(d.getName());
				}
			System.out.println(ParserReturnObject.getQueue().poll());
			System.out.println();
		}
	}

	/**
	 * This method allows a string to be processed statically
	 * 
	 * @param text	String to be parsed
	 */
	public static void passInputString(String text) {
		Log.add("Parser.java: passInputString(\"" + text + "\")");
		ParserReturnObject.setFloatingListChanged(false);
		ParserReturnObject.setDeadlineListChanged(false);

		if (text.isEmpty()) {
			ParserReturnObject.addMessage(null);
		} else {
			parser.parse(text);
		}
	}

	/**
	 * parse() takes in an input string and based on the starting command, sends
	 * the string WITHOUT the command keyword to the other parse commands for
	 * processing
	 * 
	 * @param input
	 *            input string from the keyboard
	 */
	public void parse(String input) {
		ParserReturnObject.saveContext();
		Log.add("Parser.java: parse(\"" + input + "\")");
		Log.add("Parser::parse received: " + input);
		int typeIndex, totalNumberOfTypes = validCommands.length;
		for (typeIndex = 0; typeIndex < totalNumberOfTypes; ++typeIndex) {
			if (input.startsWith(validCommands[typeIndex]))
				break;
		}
		if (typeIndex == totalNumberOfTypes) {
			// not found
			Log.add("Parser.java: parse() : Invalid command");
			ParserReturnObject.addMessage("Command not recognised.");
		} else {
			// found
			try {
				switch (typeIndex) {
				case CMD_ADD:
					Log.add("Parser.java: parse() to parseAdd()");
					parseAdd(input.substring(4));
					ParserReturnObject.update();
					break;
				case CMD_ARCHIVE:
					Log.add("Parser.java: parse() to parseDone()");
					parseDone(input.substring(8));
					ParserReturnObject.update();
					break;
				case CMD_COMPLETE:
					Log.add("Parser.java: parse() to parseDone()");
					parseDone(input.substring(9));
					ParserReturnObject.update();
					break;
				case CMD_DELETE:
					Log.add("Parser.java: parse() to parseDelete()");
					parseDelete(input.substring(7));
					ParserReturnObject.update();
					break;
				case CMD_DISPLAY:
					Log.add("Parser.java: parse() to parseDisplay()");
					parseShow(input.substring(7));
					ParserReturnObject.update();
					break;
				case CMD_DONE:
					Log.add("Parser.java: parse() to parseDone()");
					parseDone(input.substring(5));
					ParserReturnObject.update();
					break;
				case CMD_SHOW:
					Log.add("Parser.java: parse() to parseShow()");
					parseShow(input.substring(5));
					ParserReturnObject.update();
					break;
				case CMD_SEARCH:
					Log.add("Parser.java: parse() to parseSearch()");
					parseSearch(input.substring(6));
					ParserReturnObject.update();
					break;
				case CMD_UNDO:
					Log.add("Parser.java: parse() to parseUndo()");
					parseUndo();
					ParserReturnObject.update();
					break;
				case CMD_UPDATE:
					Log.add("Parser.java: parse() to parseUpdate()");
					parseUpdate(input.substring(7));
					ParserReturnObject.update();
					break;
				case CMD_REDO:
					Log.add("Parser.java: parse() to parseRedo()");
					parseRedo();
					ParserReturnObject.update();
					break;
				case CMD_CHANGE_THEME:
					Log.add("Parser.java parse() to parseChangeTheme()");
					parseChangeTheme(input.substring(6));
					break;
				case CMD_EXIT:
				case CMD_QUIT:
					Log.add("Parser.java parse() to close()");
					close();
					break;
				default:
					Log.add("Parser.java: parse() : Invalid command");
					// return invalidCommand(ERR_INVALID_COMMAND);
					break;
				}
				
			} catch (Exception ex) {
				Log.add("Parser.java: parse() : " + ex.getMessage());
				String message = ex.getMessage();
				ex.printStackTrace();
				if (message != null && !message.equals("")) {
					ParserReturnObject.addMessage(ex.getMessage());
				}
				ParserReturnObject.loadContext();
			}
		}
	}

	/**
	 * isEventsRequest() takes in the output of the parse() function and decides
	 * if it is a request to add a task classified as Events.
	 * 
	 * @param input
	 * @return successful: the index at which the keyword 'from' starts, or else
	 *         it returns 0
	 */
	private int isEventsRequest(String input) {
		Log.add("Parser.java: isEventsRequest(\"" + input + "\");");
		if (!input.contains("from")) {
			Log.add("1");
			return 0;
		}
		List<Integer> startIndices = new ArrayList<Integer>();
		String[] tempInput = input.split(" from ");
		if (tempInput.length > 1) {
			String lastToken = tempInput[tempInput.length - 1].trim();
			Log.add("lastToken: " + lastToken);
			String[] tempTempInput = lastToken.split(" to ");
			if (tempTempInput.length > 1) {
				int notADate = 0;
				try {
					Log.add("tTI[0]: " + tempTempInput[0].trim());
					List<Date> startAndEnd = DateFormatter
							.parseSchedule(lastToken);
					// Date start =
					// DateFormatter.parse(tempTempInput[0].trim());
					// Log.add("tTI[1]: " + tempTempInput[1].trim());
					// Date end = DateFormatter.parse(tempTempInput[1].trim());
				} catch (Exception ex) {
					notADate = 1;
				}
				if (notADate != 1) {
					return input.lastIndexOf(" from ");
				} else {
					Log.add("2nd output");
					return 0;
				}
			} else {
				Log.add("3rd output");
				return 0;
			}
		} else {
			Log.add("4th output");

			return 0;
		}
	}

	/**
	 * isDeadlinesRequest determines if the input string (which should be an
	 * input from the parse() function is a deadline request. Keywords used to
	 * identify a deadline is " by ".
	 * 
	 * @param input
	 *            string
	 * @return the index of the last " by ".
	 */
	private int isDeadlinesRequest(String input) {
		Log.add("Parser.java: isDeadlinesRequest(\"" + input + "\");");
		if (!input.contains(" by ")) {
			return 0;
		}
		List<Integer> startIndices = new ArrayList<Integer>();
		// level 1
		/*
		 * after last by -> date
		 */
		// level 2
		/*
		 * 1) find the last by 2) there may be other 'by's
		 */
		int lioBy = input.lastIndexOf(" by ");
		// this returns the index of the space, so we add on 4 to reach
		if (lioBy == -1) {
			// screw this
			return 0;
		} else {
			// aww yeahhh
			lioBy += 4;
		}

		try {
			Date testDate = DateFormatter.parse(input.substring(lioBy));
		} catch (Exception ex) {
			ex.printStackTrace();
			Log.add("Parser.java: isDeadlinesRequest(): " + ex.getMessage());
			return 0;
		}

		return input.lastIndexOf(" by ");
		// level 3
		/*
		 * 1)
		 */
		/*
		 * old implementation which was buggy
		 * 
		 * String[] tempInput = input.split(" by "); if(tempInput.length > 1) {
		 * String lastToken = tempInput[tempInput.length - 1].trim();
		 * Log.add(lastToken); boolean notADate = false; try { Date tempDate =
		 * DateFormatter.parse(lastToken); } catch (Exception ex) { notADate =
		 * true; } if(!notADate) { return true; } else { return false; } } else
		 * { return false; }
		 */
	}

	/**
	 * parseAdd() parses all add commands and is called from the main parse()
	 * function if they command keyword was 'add' parseAdd() determines the type
	 * of task to add (todo/deadline/event)
	 * 
	 * @param input
	 * @throws Exception
	 */
	private void parseAdd(String input) throws Exception {
		final String NAME_FUNCTION = ".parseAdd(String input)";
		Log.add(NAME_CLASS + NAME_FUNCTION + ": received \"" + input + "\"");
		int validAddCommandsLength = validAddCommands.length;
		int typeIndex = -1;
		int eventsRequestResult = isEventsRequest(input);
		int deadlineRequestResult = isDeadlinesRequest(input);
		if (eventsRequestResult != 0) {
			Log.add("Parser.java: parseAdd(): event type set");
			typeIndex = CMD_ADD_EVENT;
		}
		if (deadlineRequestResult != 0) {
			if (deadlineRequestResult > eventsRequestResult) {
				Log.add("Parser.java: parseAdd(): deadline type set");
				typeIndex = CMD_ADD_DEADLINE;
			}
		}
		if (eventsRequestResult == 0 && deadlineRequestResult == 0) {
			Log.add("Parser.java: parseAdd(): todo type set");
			typeIndex = CMD_ADD_TODO;
		}
		/*
		 * for (typeIndex = 0; typeIndex < validAddCommandsLength; ++typeIndex)
		 * { if (input.startsWith(validAddCommands[typeIndex])) { break; } } if
		 * (typeIndex == validAddCommandsLength) { throw new
		 * Exception("Cannot recognise this task type."); }
		 */
		/*
		 * keywords: events - from ____ to ____ find last 'from' find next 'to'
		 * after 'from' substring(last'from', next'to') should be a date
		 * substring(next'to') should also be a date deadline - by find last
		 * 'by' substring(last'by') should be a date todo - nothing default case
		 */

		Task task = null;

		/*
		 * 
		 * hash tag
		 */

		LinkedList<String> listHashTags = null;
		String[] tokenHashTags = input.split("#");
		if (tokenHashTags.length > 1) {
			// there exists hashtags
			listHashTags = new LinkedList<String>();
			for (int i = 1; i < tokenHashTags.length; ++i) {
				// extract the first word of every token after the first
				int endRange = tokenHashTags[i].indexOf(' ');
				if (endRange == -1) {
					endRange = tokenHashTags[i].length();
				}
				String hashtag = tokenHashTags[i].substring(0, endRange);
				if (!hashtag.isEmpty()) {
					listHashTags.add(hashtag);
					if (hashtag.contains(";")) {
						endRange--;
					}
				}
				tokenHashTags[i] = tokenHashTags[i].substring(endRange).trim();
			}
		}
		input = "";
		for (int i = 0; i < tokenHashTags.length; ++i) {
			input += tokenHashTags[i];
		}
		Log.add("Parser.java: parseAdd(): #tag list contents: " + listHashTags);
		switch (typeIndex) {
		case CMD_ADD_TODO:
			// get an int -> successful/failure
			if (listHashTags != null) {
				task = new Floating(input.trim(), false, listHashTags);
			} else {
				task = new Floating(input.trim(), false);
			}
			break;

		case CMD_ADD_DEADLINE:
			/*
			 * String[] tokensDeadline = input.split("by"); // [0] - "deadline",
			 * [1] - <description>, [2] - <date> Date deadlineDate; deadlineDate
			 * = DateFormatter.parse(tokensDeadline[tokensDeadline.length -
			 * 1].trim()); String deadlineDescription = ""; for(int i = 0; i <
			 * tokensDeadline.length - 1; ++i) { deadlineDescription +=
			 * tokensDeadline[i]; }
			 */

			// we can safely add 4 because we have checked it already earlier
			int deadlineDateStart = input.lastIndexOf(" by ");
			String deadlineDescription = input.substring(0, deadlineDateStart);

			// 4 is the number of characters in ' by '
			deadlineDateStart += 4;
			Date deadlineDate = DateFormatter.parse(input
					.substring(deadlineDateStart));

			if (listHashTags != null) {
				task = Deadline.createDeadline(deadlineDescription,
						deadlineDate, listHashTags);
			} else {
				task = Deadline.createDeadline(deadlineDescription,
						deadlineDate);
			}
			break;

		case CMD_ADD_EVENT:
			int eventDatesStart = input.lastIndexOf(" from ");
			String eventDescription = input.substring(0, eventDatesStart);
			eventDatesStart += 6;
			String eventDates = input.substring(eventDatesStart);
			List<Date> startAndEnd = DateFormatter.parseSchedule(eventDates);
			Date startDate = startAndEnd.get(0),
			endDate = startAndEnd.get(1);
			if (listHashTags != null) {
				task = Schedule.createSchedule(eventDescription, startDate,
						endDate, listHashTags);
			} else {
				task = Schedule.createSchedule(eventDescription, startDate,
						endDate);
			}
			/*
			 * String[] tokensEvent = input.split("from"); if(tokensEvent.length
			 * > 1) { String lastToken =
			 * tokensEvent[tokensEvent.length-1].trim(); //String[] datePair =
			 * lastToken.split(" to "); List<Date> startAndEnd =
			 * DateFormatter.parseSchedule(lastToken); assert
			 * (startAndEnd.size() == 2); Date startDate = startAndEnd.get(0),
			 * endDate = startAndEnd.get(1); String eventsDescription = "";
			 * for(int i = 0; i < tokensEvent.length - 1; ++i) {
			 * eventsDescription += tokensEvent[i].trim(); } if(lindsaysList !=
			 * null) { task = Schedule.createSchedule(eventsDescription,
			 * startDate, endDate, lindsaysList); } else { task =
			 * Schedule.createSchedule(eventsDescription, startDate, endDate); }
			 * } else {
			 * 
			 * }
			 */

			/*
			 * // [0] - "event", [1] - <description>, [2] - <all possible dates>
			 * String description = tokensEvent[1].trim(); String allDates =
			 * tokensEvent[2].trim(); String[] dateSegments =
			 * allDates.split(DEFAULT_DELIMITER); int dateSegmentsLength =
			 * dateSegments.length;
			 * 
			 * if (dateSegmentsLength == 1) { // if there's only 1 date segment,
			 * let's create a Schedule
			 * 
			 * 
			 * String[] startEnd = dateSegments[0].split(" to "); Date
			 * startDate, endDate; List <Date> list =
			 * DateFormatter.parseSchedule(dateSegments[0]); // startDate =
			 * DateFormatter.parse(startEnd[0].trim()); // endDate =
			 * DateFormatter.parse(startEnd[1].trim());
			 * 
			 * startDate = list.get(0); endDate = list.get(1);
			 * 
			 * 
			 * if(lindsaysList != null) { task =
			 * Schedule.createSchedule(description, startDate, endDate,
			 * lindsaysList); } else { task =
			 * Schedule.createSchedule(description, startDate, endDate); } }
			 * else { // otherwise consider UnconfirmedSchedule LinkedList<Date>
			 * listOfDatePairs = new LinkedList<Date>();
			 * 
			 * for (String segment : dateSegments) { Log.add(NAME_CLASS +
			 * NAME_FUNCTION + ": Parsing \"" + segment + "\""); String[]
			 * datePair = segment.split("to"); if (datePair.length != 2) { //
			 * invalid date entry throw new Exception(
			 * "Unable to recognise the date you entered."); } else {
			 * listOfDatePairs.add(DateFormatter.parse(datePair[0].trim()));
			 * listOfDatePairs.add(DateFormatter.parse(datePair[1].trim())); } }
			 * 
			 * if (listOfDatePairs == null || listOfDatePairs.size() == 0) {
			 * throw new Exception("Unknown error."); } if
			 * (listOfDatePairs.size() % 2 != 0) { throw new
			 * Exception("The dates entered are not in pairs."); } task = new
			 * UnconfirmedSchedule(description, listOfDatePairs);
			 * 
			 * /* TODO -> create a new error code?
			 */

			/*
			 * TAKE NOTE OF THIS PART, HOW TO ADD MULTIPLE SCHEDULES OR SHOULD
			 * WE IGNORE DATES AFTER THE FIRST SET OF DATES
			 * 
			 * for now, it's add as each set of dates is parsed and everything
			 * returned at once after that
			 */
			/*
			 * for (counter = 0; counter < dateSegmentsLength; ++counter) {
			 * System.out.println(dateSegments[counter]); String[] startEnd =
			 * dateSegments[counter].split(" to "); Date startDate, endDate; try
			 * { startDate = new Date(startEnd[0].trim()); endDate = new
			 * Date(startEnd[1].trim()); } catch (ParseException e) { return new
			 * ParserReturnObject( ParserReturnObject.ERR_WRONG_DATE_FORMAT); }
			 * try { task = Schedule.createSchedule(description, startDate,
			 * endDate); } catch (WrongDateException e) { return new
			 * ParserReturnObject( ParserReturnObject.ERR_START_END_DATE); } }
			 */

			break;

		default:
			break;
		}

		Command command = new CommandAdd(task, internalData);
		command.execute();
		ParserReturnObject.setListChanged();
		ParserReturnObject.addMessage("Task added successfully.");
		undoRedo.clearRedo();
		undoRedo.pushToUndo(command);
	}

	/**
	 * parseDelete() parses all delete commands from the input line. Input to
	 * parseDelete() should only be from the main parse() function
	 * 
	 * @param input
	 * @throws Exception
	 */
	private void parseDelete(String input) throws Exception {
		Log.add("Parser::parseDelete received: " + input);
		int itemIndex = 0;
		try {
			itemIndex = Integer.parseInt(input.trim());
		} catch (NumberFormatException e) {
			throw new Exception("Please provide a valid index number.");
		}
		if (itemIndex > ParserReturnObject.getLengthDaS()
				+ ParserReturnObject.getLengthFT()) {
			throw new Exception("No such task with this number.");
		}
		if (itemIndex > ParserReturnObject.getLengthDaS()) {
			itemIndex -= (ParserReturnObject.getLengthDaS() + 1);
			// itemIndex not points to correct index in floating tasks
			Floating temp = ParserReturnObject.getFT(itemIndex);
			Command toPush;
			if (temp.isComplete()) {
				toPush = new CommandDeleteFromArchive(temp, internalData);
			} else {
				toPush = new CommandDelete(temp, internalData);
			}
			toPush.execute();
			undoRedo.pushToUndo(toPush);
			undoRedo.clearRedo();

		} else {
			itemIndex--;
			// convert from number on screen to it's index
			Deadline temp = null;
			temp = ParserReturnObject.getDaS(itemIndex);
			Command toPush;
			/*
			 * if (temp instanceof Schedule) { Schedule schedule = (Schedule)
			 * temp; if (!schedule.isConfirmed() &&
			 * schedule.getUnconfirmedSchedule()
			 * .getUnconfirmedSchedules().size() > 1) { LinkedList<Date> list =
			 * new LinkedList<Date>(); for (Schedule sch :
			 * schedule.getUnconfirmedSchedule() .getUnconfirmedSchedules()) {
			 * if (sch != schedule) { list.add(sch.getStartDate());
			 * list.add(sch.getEndDate()); } } UnconfirmedSchedule us; us = new
			 * UnconfirmedSchedule(schedule.getName(), list);
			 * 
			 * toPush = new CommandUpdate( schedule.getUnconfirmedSchedule(),
			 * us, internalData); toPush.execute();
			 * 
			 * } }
			 */
			toPush = new CommandDelete(temp, internalData);
			toPush.execute();
			undoRedo.pushToUndo(toPush);
			undoRedo.clearRedo();
		}
		ParserReturnObject.setListChanged();
		ParserReturnObject.addMessage("Task deleted successfully.");
	}

	/**
	 * parseDone() processes input strings which indicate a task should be
	 * marked as completed.
	 * 
	 * @param input
	 * @throws Exception
	 */
	private void parseDone(String input) throws Exception {
		System.out.println("Parser::parseDone received: " + input);
		int index;
		Command command;
		try {
			index = Integer.parseInt(input.trim());
		} catch (NumberFormatException e) {
			throw new Exception("Please input an index number of the task");
		}

		int length1 = ParserReturnObject.getLengthDaS();
		int length2 = ParserReturnObject.getLengthFT();
		if (length1 + length2 < index) {
			throw new Exception("No such task with this index number exists.");
		} else if (index <= length1) {
			Deadline deadline = ParserReturnObject.getDaS(index - 1);
			if (!(deadline instanceof Schedule)) {
				command = new CommandComplete(deadline, internalData);
			} else {
				throw new Exception("Events cannot be \"completed\", silly.");
			}
		} else {
			Floating task = ParserReturnObject.getFT(index - length1 - 1);
			command = new CommandComplete(task, internalData);
		}
		command.execute();
		ParserReturnObject.setListChanged();
		undoRedo.pushToUndo(command);
		undoRedo.clearRedo();
		ParserReturnObject.addMessage("Item completed!");
	}

	/**
	 * parseSearch
	 * 
	 * @param input
	 * @throws Exception
	 */
	private void parseSearch(String input) throws Exception {
		System.out.println("Parser::parseSearch received: " + input);
		String search = input.trim();
		String[] tokens = search.split(" ");
		if (tokens[0].equals("-a")) {
			search = input.trim().substring(3).trim();
			ParserReturnObject.setSearchString(search);
			ParserReturnObject
					.setStatus(ParserReturnObject.STATUS_SHOW_SEARCH_ARCHIVE);
			ParserReturnObject.addMessage("Search results of \"" + search + "\" in archive.");
		} else if (tokens[0].equals("-d")) {
			 search = input.trim().substring(3).trim();
			 if (search.contains(" to ")) {
				 String[] dates = search.split(" to ");
				 dates[0] = dates[0].trim();
				 dates[1] = dates[1].trim();
				 Date start = DateFormatter.parse(dates[0]);
				 Date end = DateFormatter.parse(dates[1]);
				 if (start.compareTo(end) > 0) {
					 throw new Exception("The start time is later than the ending time.");
				 }
				 ParserReturnObject.setStartDate(start);
				 ParserReturnObject.setEndDate(end);
				 ParserReturnObject.setStatus(ParserReturnObject.STATUS_SHOW_DATE_RANGE);
				 ParserReturnObject.addMessage("Showing search results for " + DateFormatter.format(start, end));
			 } else {
				 Date date = DateFormatter.parse(search);
				 ParserReturnObject.setDate(date.extractDate());
				 ParserReturnObject.setStatus(ParserReturnObject.STATUS_SHOW_DATE);
				 ParserReturnObject.addMessage("Showing search results for " + DateFormatter.format(date));
			 }
		} else if (tokens[0].equals("-t")) {
			search = input.trim().substring(3).trim();
			if (search.contains(" ")) {
				throw new Exception("You cannot search more than one tag at a time.");
			}
			ParserReturnObject.setSearchString(search);
			ParserReturnObject.setStatus(ParserReturnObject.STATUS_SHOW_SEARCH_TAGS);
			ParserReturnObject.addMessage("Search results of all tasks with #" + search + ".");
		
		} else {
			ParserReturnObject.setSearchString(search);
			ParserReturnObject.setStatus(ParserReturnObject.STATUS_SHOW_SEARCH);
			ParserReturnObject.addMessage("Search results of \"" + search + "\"");
		}
		ParserReturnObject.setAction(ParserReturnObject.ACTION_UPDATE_ALL);
		ParserReturnObject.setListChanged();
	}

	/**
	 * parseShow()
	 * 
	 * @param input
	 */
	private void parseShow(String input) throws Exception {
		System.out.println("Parser::parseShow received: " + input);

		String state = input.trim();

		if (state.equalsIgnoreCase("home")) {
			ParserReturnObject.setAction(ParserReturnObject.ACTION_UPDATE_ALL);
			ParserReturnObject
					.setStatus(ParserReturnObject.STATUS_SHOW_CURRENT);
		} else if (state.equalsIgnoreCase("archive")) {
			ParserReturnObject.setAction(ParserReturnObject.ACTION_UPDATE_ALL);
			ParserReturnObject
					.setStatus(ParserReturnObject.STATUS_SHOW_ARCHIVE);
		} else {
			throw new Exception("Valid show commands are \"show home\" and \"show archive\".");
		}
	}

	/**
	 * parseUndo() initiates an undo command
	 * 
	 * @throws Exception
	 */
	private void parseUndo() throws Exception {
		System.out.println("Parser::parseUndo received: ");
		if (undoRedo.undoIsEmpty()) {
			throw new Exception("Nothing to undo.");
		} else {
			Command command = undoRedo.popFromUndo();
			command.reverse().execute();
			ParserReturnObject.setListChanged();
			undoRedo.pushToRedo(command);
		}
		ParserReturnObject.addMessage("Undone successfully.");
	}

	/**
	 * This method updates the modified fields of a task
	 * 
	 * @param input			A string that contains information of the task and modified fields
	 * @throws Exception	when not enough fields are provided in the string
	 */
	private void parseUpdate(String input) throws Exception {
		System.out.println("Parser::parseUpdate received: " + input);
		String[] tokens = input.split(DEFAULT_DELIMITER, 4);
		int index;
		Task newTask;
		Task task = null;
		for (int i = 0; i < tokens.length; i++) {
			tokens[i] = tokens[i].trim();
		}
		try {
			index = Integer.parseInt(tokens[0]);
		} catch (NumberFormatException e) {
			throw new Exception("No index number input.");
		}
		int length1 = ParserReturnObject.getDeadlinesAndSchedules().size();
		int length2 = ParserReturnObject.getFloatingTasks().size();
		if (length1 + length2 < index) {
			throw new Exception("No such item with this index number.");
		}
		if (index <= length1) {
			task = ParserReturnObject.getDeadlinesAndSchedules().get(index - 1);
		} else {
			task = ParserReturnObject.getFloatingTasks().get(
					index - 1 - length1);
		}
		LinkedList<String> tags = task.getTags();
		if (task instanceof Floating) {
			if (tokens.length < 2) {
				throw new Exception("Not enough arguments.");
			}
			if (task.isComplete()) {
				throw new Exception(
						"You may not modify completed floating tasks.");
			}
			newTask = new Floating(tokens[1], ((Floating) task).isPriority(),
					tags);
		} else if (task instanceof Schedule) {
			if (tokens.length < 4) {
				throw new Exception("Not enough arguments.");
			}
			String desc = tokens[1];
			String start = tokens[2];
			String end = tokens[3];
			Date startDate, endDate;
			if (desc.isEmpty()) {
				desc = task.getName();
			}
			if (start.isEmpty()) {
				startDate = new Date(((Schedule) task).getStartDate()
						.toString());
			} else {
				startDate = DateFormatter.parse(start);
			}
			if (end.isEmpty()) {
				endDate = new Date(((Schedule) task).getEndDate().toString());
			} else {
				endDate = DateFormatter.parse(end);
			}

			newTask = Schedule.createSchedule(desc, startDate, endDate, tags);

		} else if (task instanceof Deadline) {
			if (tokens.length < 3) {
				throw new Exception("Not enough arguments.");
			}
			String desc = tokens[1];
			String end = tokens[2];
			if (desc.isEmpty()) {
				desc = task.getName();
			}
			Date endDate;
			if (end.isEmpty()) {
				endDate = new Date(((Deadline) task).getEndDate().toString());
			} else {
				endDate = DateFormatter.parse(end);
			}

			newTask = Deadline.createDeadline(desc, endDate, tags);

		} else {
			throw new Exception("Cannot identify task type.");
		}
		Command command = new CommandUpdate(task, newTask, internalData);
		command.execute();
		ParserReturnObject.setListChanged();
		undoRedo.pushToUndo(command);
		undoRedo.clearRedo();
		ParserReturnObject.addMessage("Updated successfully.");
	}

	/**
	 * This method performs a redo operation
	 * 
	 * @throws Exception	when there is nothing left to redo
	 */
	private void parseRedo() throws Exception {
		System.out.println("Parser::parseRedo received: ");
		if (undoRedo.redoIsEmpty()) {
			throw new Exception("Nothing to redo.");
		} else {
			Command command = undoRedo.popFromRedo();
			command.execute();
			ParserReturnObject.setListChanged();
			undoRedo.pushToUndo(command);
		}
		ParserReturnObject.addMessage("Redone successfully.");
	}
	
	// @author A0098710B
	private void parseChangeTheme(String theme) {
		if (theme.equals("mountain")) {
			GUIDispatcher.changeTheme("mountain.css");
			ParserReturnObject.addMessage("Changed theme to mountain.");
		} else if (theme.equals("lunar")) {
			GUIDispatcher.changeTheme("lunar.css");
			ParserReturnObject.addMessage("Changed theme to lunar.");
		} else {
			ParserReturnObject.addMessage("No such theme exists.");
		}
	}

	/**
	 * This method ensures that all the data is properly written when the application is closed
	 */
	public static void close() {
		Data.close();
		Platform.exit();
	}
}